package com.zysc.processflow.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zysc.processflow.entity.Attachment;
import com.zysc.processflow.mapper.AttachmentMapper;
import com.zysc.processflow.service.IAttachmentService;
import com.zysc.processflow.common.base.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 附件表 服务实现类
 * </p>
 *
 * @author 正元智慧城市_
 * @since 2022-11-18
 */
@Service
public class AttachmentServiceImpl extends BaseService<AttachmentMapper, Attachment> implements IAttachmentService {
    private final Path rootLocation;
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");

    @Autowired
    public AttachmentServiceImpl(MultipartProperties properties) {
        this.rootLocation = Paths.get(properties.getLocation());
    }

    @Override
    public void init() {
        try {
            Files.createDirectories(rootLocation);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String store(MultipartFile file, String fileid) throws Exception {
        String filename = StringUtils.cleanPath(file.getOriginalFilename());
        StringBuffer errmsg = new StringBuffer();
        if (file.isEmpty()) {
            errmsg.append("Failed to store enpty file " + filename);
        }
        if (filename.contains("..")) {
            errmsg.append("Can not store file with relative path outside current directory" + filename);
        }
        if (errmsg.length() > 0)
            System.out.println(errmsg.toString());

        // 按日期新建文件加
        String datePath = sdf.format(new Date());
        File destFolder = new File(this.rootLocation.toString() + "/" + datePath + "/");
        destFolder.mkdirs();
        // 动态文件名称。
        String saveName = datePath + "/" + fileid;
        String fileType = "";
        if (filename.lastIndexOf(".") > 0) {
            fileType = filename.substring(filename.lastIndexOf("."));
        }
        saveName += fileType;

        try (InputStream inputStream = file.getInputStream()) {
            Files.copy(inputStream, this.rootLocation.resolve(saveName), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new Exception("Failed to store file " + filename, e);
        }
        return saveName;
    }

    @Override
    public Stream<Path> loadAll() throws Exception {
        try {
            Stream<Path> s1 = Files.walk(this.rootLocation, 1);
            Stream<Path> s2 = s1.filter(path -> !path.equals(this.rootLocation));
            Stream<Path> s3 = s2.map(this.rootLocation::relativize);
            return s3;
        } catch (IOException e) {
            throw new Exception("Failed to read stored files", e);
        }
    }

    @Override
    public Path load(String filename) {
        return rootLocation.resolve(filename);
    }

    @Override
    public Resource loadAsResource(String filename) throws Exception {
        try {
            Path file = load(filename);
            Resource resource = new UrlResource(file.toUri());
            if (resource.exists() || resource.isReadable()) {
                return resource;
            } else {
                System.out.println("Could not read file: " + filename);
                return null;
            }
        } catch (IOException e) {
            throw new Exception("Could not read file: " + filename, e);
        }
    }

    @Override
    public void deleteAll() {
        FileSystemUtils.deleteRecursively(rootLocation.toFile());
    }

    @Override
    public Boolean deletefiles(List<String> relativepaths) {
        Boolean isDelete = Boolean.TRUE;
        int errnum = 0;
        try {
            for (Integer i = 0; i < relativepaths.size(); i++) {
                Path deletePath = load(relativepaths.get(i));
                isDelete = FileSystemUtils.deleteRecursively(deletePath);
                if (!isDelete) {
                    errnum++;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (errnum == 0)
            return true;
        else
            return false;
    }

    // 删除更新指定关联id及类别的附件信息。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Boolean UpdateLinkAttachment(String linkid, String type, List<Attachment> attachments) {
        if (linkid == null || linkid.equals(""))
            return false;
        if (attachments == null) {
            attachments = new ArrayList<>();
        }
        QueryWrapper<Attachment> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(linkid != null, Attachment::getLinkid, linkid)
                .eq(type != null, Attachment::getType, type);
        List<Attachment> ls = this.baseMapper.selectList(queryWrapper);
        //需要删除的。
        List<String> curids = attachments.stream().map(item -> item.getId()).collect(Collectors.toList());
        List<String> delids = new ArrayList<>(), delpaths = new ArrayList<>();
        for (Attachment l : ls) {
            if (!curids.contains(l.getId())) {//不在当前附件列表里
                delids.add(l.getId());
                delpaths.add(l.getPath());
            }
        }
        boolean b1 = true;
        if (delids.size() > 0) {
            b1 = removeByIds(delids);
            if (b1)
                deletefiles(delpaths);
        }

        // 更新现有数据linkid 及type
        boolean b2 = true;
        long tm = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        attachments.forEach(item -> {
            item.setLinkid(linkid);
            item.setUpdatetime(tm);
            if (type != null && !type.equals(""))
                item.setType(type);
        });
        if (attachments.size() > 0)
            b2 = updateBatchById(attachments);

        if (!b1 || !b2) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return b1 && b2;
    }

    @Override
    public List<Attachment> getFilesByLinkId(String id) {
        LambdaQueryWrapper<Attachment> qw = new LambdaQueryWrapper<>();
        qw.eq(Attachment::getLinkid,id);
        return this.list(qw);
    }
}
